#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdio.h>
#include <time.h>
#include "XrdDownloadManager.hh"
#include "XrdDMSimulator.hh"

XrdDownloadManager::XrdDownloadManager() :
        fAliEn ( 0 ),
        fDebugLevel ( kFull ),
        fSimulator ( 0 ),
        fDoAliEnToken ( true ),
        fAliEnUserName ( "" ),
        fAliEnPassword ( "" ),
        fRenewCertificateTime ( 1800 ), // seconds
        fAliEnConnectRetry ( 3 ),
        fSuccessfullDownloadSpeed ( -1 ),
        fUseFallbackDownload ( false ),
        fUseServersFile ( false ),
        fUseServersFileTryNewSitesFirst ( false ),
        fUseMonalisa ( false ),
        fUseAliEnCp ( false ),
        fFileNameFromCopyNice ( "" ),
        fNumberOfFilesToDownload ( 0 ),
        fFileNameToCopy ( "" ),
        fFileNameToCopyNice ( "" ),
        fCurrentFileSize ( 0 ),
        fCurrentFileNameNice ( "" ),
        fXrdCpOptions ( "" ),
        fXrdCpOptionsExtra ( "" ),
        fNumColumnsInServersFile ( 3 ),
        fNumberOfDataServersInFile ( 0 ),
        fServersFileDirName ( "/tmp" ),
        fServersFileName ( "xrootd_servers.txt" ),
        fAppMonConfigFile ( "http://alicepc100.jinr.ru/gentoo-files/monalisa_cern.conf" ),
        fLogDir ( "/tmp" ) {

}

XrdDownloadManager::~XrdDownloadManager() {
//   delete fAliEn;
#ifdef HAS_APPMON
    if ( fUseMonalisa )  fAppMon.Finish();
#endif
}



void XrdDownloadManager::Init() {
    XrdDownloadManagerDebugFull ( "<-" );
    fNumberOfFilesToDownload = 0;
#ifdef HAS_APPMON
    XrdDownloadManagerDebugFull ( "AppMon enabled ..." );
    if ( fUseMonalisa )  fAppMon.Init ( fAppMonConfigFile.c_str(),fAppMonName.c_str() );
#else
    XrdDownloadManagerDebugFull ( "AppMon NOT enabled ..." );
#endif

    XrdDownloadManagerDebugFull ( "->" );
}


void XrdDownloadManager::InitLogFile()
{
// 	if 
    string logfile ( fLogDir.c_str() );
//     logfile.append ( "/" );
    logfile.append ( fFileNameToCopyNice.c_str() );

    fLog.Debug ( 2,"Creating log direcotry %s ...",logfile.c_str() );
    string mkdirStr ( "mkdir -p " );
    mkdirStr.append ( logfile.c_str() );
//   mkdirStr.append(fFileNameToCopy.c_str());
//   mkdirStr.append(fCurrentFileNameNice.c_str());
    mkdirStr.append ( " > /dev/null 2>&1" );
    int mkdirRetVal = system ( mkdirStr.c_str() );
    if ( mkdirRetVal ) fLog.Error ( "Direcotry %s was created with return value %d...",logfile.c_str(),mkdirRetVal );


    logfile.append ( "/xrd-dm_" );
    char num[10];
    sprintf ( num, "%u", getpid() );
    logfile.append ( num );

	fCurrentLogFileName = logfile;
	fLog.SetFile(logfile.c_str());
}



void XrdDownloadManager::Reset() {
    XrdDownloadManagerDebugFull ( "<-" );
    fNumberOfFilesToDownload = 0;
    for ( int i = 0;i < XRDDOWNLOADMANAGER_MAX_FILES;i++ ) {
        fFileNameToDownload[i] = "";
        fIsFileStaged[i] = true;
    }
    ResetServersFileData();
    fFileNameToCopy = "";
	fFileNameToCopyNice = "";

    XrdDownloadManagerDebugFull ( "->" );
}


void XrdDownloadManager::ResetServersFileData() {
    XrdDownloadManagerDebugFull ( "<-" );
    fNumberOfDataServersInFile = 0;
    for ( int i = 0;i < XRDDOWNLOADMANAGER_MAX_SERVERS;i++ ) {
        fSFPath[i] = "";
        fSFSpeed[i] = 0;
        fSFAccessNumber[i] = 0;
    }
    XrdDownloadManagerDebugFull ( "->" );
}

int XrdDownloadManager::SetupFileType() {
    XrdDownloadManagerDebugFull ( "<-" );

    string strAll;
    strAll = fFileNameFromCopyNice;
    // starts with alien://
    size_t pos;
    pos = strAll.find ( "alien:///" );
    if ( pos != string::npos ) {
        strAll.erase ( 0, 8 );
        fLog.Debug ( 2,"File starts with alien:/// ...." );
        fLog.Info ( "Found file %s ....",strAll.c_str() );
        AddFileToDownloadFromAliEn ( strAll.c_str() );
        return 1;
    }
    strAll = fFileNameFromCopyNice;
    // starts with guid:///
    pos = strAll.find ( "guid:///" );
    if ( pos != string::npos ) {
        strAll.erase ( 0, 8 );
        //     cout << strAll.c_str()<< endl;
//         dm->AddFileToDownloadFromAliEn ( strAll.c_str() );
        fLog.Error ( "guid:/// is not supported yet !!!!!!" );
        return -1;
    }

    strAll = fFileNameFromCopyNice;
    while ( !strAll.empty() ) {
        pos = strAll.find_first_of ( "," );
        AddFileToDownload ( strAll.substr ( 0, strAll.find_first_of ( "," ) ) );
        strAll.erase ( 0, pos + 1 );
        if ( pos > strAll.length() ) {
            AddFileToDownload ( strAll.c_str() );
            break;
        }
    }
    XrdDownloadManagerDebugFull ( "->" );
    return 0;
}


int XrdDownloadManager::DownloadFile () {
    XrdDownloadManagerDebugFull ( "<-" );
    int retVal = -1;

    if ( SetupFileType() <0 ) return -1;

	if (fDoAliEnToken) {
	    fLog.Info ( "Testing AliEn token ...." );
		if (DoAliEnToken()) {
			fLog.Error ( "Problem with AliEn token !!!" );
			XrdDownloadManagerError ( "Problem with AliEn token !!!" );
			return -2;
		}
		fLog.Info ( "AliEn token is OK ...." );
	}

    fLog.Info ( "Download Starting ...." );
//     fFileNameToCopy = outFile;
//     fFileNameToCopyNice ="$";
//     fFileNameToCopyNice += fFileNameToCopy;
//
//     FindAndReplaceString(fFileNameToCopyNice,"xrdnamespace","$");
//     fFileNameToCopyNice.erase(0,fFileNameToCopyNice.find_last_of("$")+1);
//     FindAndReplaceString(fFileNameToCopyNice,"%","/");

//     fFileNameToCopyNice = fCurrentFileNameNice;


//   fFileNameToCopyNice
    // get if download was ok

    if ( fUseServersFile ) {
        if ( DoUseServersFile() < 0 ) {
#ifdef HAS_APPMON
            if ( fUseMonalisa )  fAppMon.SendErrorMessage ( ( char* ) "DoUseServersFile error" );
#endif
            return 2;
        }
    }
    if ( fUseFallbackDownload ) {
        retVal = DoFallBackDownload();
        if ( retVal != 0 ) {
#ifdef HAS_APPMON
            if ( fUseMonalisa )  fAppMon.SendErrorMessage ( ( char* ) "Download Error After Last FallBack" );
#endif
            return 1;
        }
    } else {
        retVal = DoOneDownload();
        if ( !retVal ) {
#ifdef HAS_APPMON
            if ( fUseMonalisa )  fAppMon.SendErrorMessage ( ( char* ) "Download Error" );
#endif
            return 1;
        }
//     AddServerToServersFile(fFileNameToDownload[retVal-1],1000);
    }



    fLog.Debug ( 1,"Download done with %d before AddServerToServersFile() ....",retVal );

// //     if ( fSuccessfullDownloadSpeed < 0 ) {
// //         fLog.Error ( "fSuccessfullDownloadSpeed is %d and is should be positive ....",fSuccessfullDownloadSpeed );
// // #ifdef HAS_APPMON
// //         if ( fUseMonalisa )  fAppMon.SendErrorMessage ( ( char* ) "Download Error (fSuccessfullDownloadSpeed was negative)" );
// // #endif
// //         return 0;
// //     }
// // //   cout << "site is " << GetSiteString(fFileNameToDownload[retVal-1]) << endl;
// // 
// //     fLog.Debug ( 1,"AddServerToServersFile (%s,%d) ", fFileNameToDownload[retVal-1].c_str(), fSuccessfullDownloadSpeed );
// //     AddServerToServersFile ( fFileNameToDownload[retVal-1], fSuccessfullDownloadSpeed );

//     if ( retVal > 1 ) retVal = 1;

    fLog.Info ( "Download done with %d ....",retVal );

#ifdef HAS_APPMON
    if ( fUseMonalisa )  fAppMon.SendInfoMessage ( ( char* ) "OK" );
#endif

    XrdDownloadManagerDebugFull ( "->" );
    return retVal;
}

void XrdDownloadManager::AddFileToDownload ( string filename, bool isFileStaged ) {
    XrdDownloadManagerDebugFull ( "<-" );

    if ( fDebugLevel >= kDebugMore )
        cout << "XrdDownloadManager::AddFileToDownload(...) Adding file " << filename << " with index " << fNumberOfFilesToDownload << endl;
    fFileNameToDownload[fNumberOfFilesToDownload] = filename;
    fIsFileStaged[fNumberOfFilesToDownload] = isFileStaged;
    fNumberOfFilesToDownload++;

    XrdDownloadManagerDebugFull ( "->" );
}

int XrdDownloadManager::DoUseServersFile() {
    XrdDownloadManagerDebugFull ( "<-" );

    fLog.Info ( " We are using ServersFile ..." );

    int retVal = 0;
    retVal = OptimizeServersOrder();
    fLog.Debug ( 1," retVal after OptimizeServersOrder() %d ...",retVal );

    XrdDownloadManagerDebugFull ( "->" );

    return retVal;
}

int XrdDownloadManager::DoFallBackDownload() {
    XrdDownloadManagerDebugFull ( "<-" );

    fLog.Info ( " We are using FallBackDownload (%d) ...",fNumberOfFilesToDownload );
    for ( int i = 0;i < fNumberOfFilesToDownload;i++ ) {
        fLog.Info ("\t[%d] %s ",i ,fFileNameToDownload[i].c_str());
	}
	
//     XrdDownloadManagerDebugFull("DoFallBackDownload()  fNumberOfFilesToDownload is " << fNumberOfFilesToDownload<< "... ");
    int retVal = -1;
	int retIndex=-1;
    for ( int i = 0;i < fNumberOfFilesToDownload;i++ ) {
        fLog.Info ("Trying [%d] %s ...",i ,fFileNameToDownload[i].c_str());
        if ( DoOneDownload ( i ) ) {
			AddServerToServersFile ( fFileNameToDownload[i], 0, 0 );
            continue;
        } else {
			retVal = 0;
			retIndex = i+1;
			break;
		}
    }
	if (retIndex == -1 )
		retVal=1;
    
    fLog.Debug ( 1," after FallBackDownload(%d of %d) retVal is %d ...",retIndex,fNumberOfFilesToDownload,retVal );
    XrdDownloadManagerDebugFull ( "->" );
    return retVal;
}

int XrdDownloadManager::DoOneDownload ( int index ) {

    XrdDownloadManagerDebugFull ( "<-" );
    int retVal = 0;

    fLog.Debug ( 1," DoOneDownload with index %d ...",index );

    if ( fSimulator ) {
        fLog.Debug ( 1," Using Simulator ..." );
        if ( fIsFileStaged[index] ) {
            fLog.Info ( "Simulator DoOneDownload of file fFileNameToDownload[index] was OK" );
            fSuccessfullDownloadSpeed = fSimulator->GetSiteRandomSpeed ( GetSiteString ( fFileNameToDownload[index] ) );
            fLog.Info ( "Simulator DoOneDownload download speed is %d ",fSuccessfullDownloadSpeed );
            retVal = 1;
        } else {
            fLog.Info ( "Simulator DoOneDownload of file fFileNameToDownload[index] was FAILED" );
//       if (fUseMonalisa)  fAppMon.SendErrorMessage("Download Failed");
            fSuccessfullDownloadSpeed = -1;
            retVal = 0;
        }
    } else {
        fLog.Debug ( 1," DoOneDownload with index %d Before CopyXrd () ...",index );
        retVal = CopyXrd ( index );
        string downloadStr =  retVal == 0 ? "--- Download OK ---" : "--- Download Failed ---";
// 		fLog.Debug(1," DoOneDownload with index %d after CopyXrd () = %s ...",index,downloadStr);
//         fLog.Info ( " DoOneDownload with index %d after CopyXrd %s ...",index,downloadStr.c_str() );
//         fLog.Info ( "Download speed: %d kB/s",fSuccessfullDownloadSpeed );
    }

    XrdDownloadManagerDebugFull ( "->" );
    return retVal;
}


int XrdDownloadManager::CopyXrd ( int index ) {
    XrdDownloadManagerDebugFull ( "->" );
    string logfile ( fLogDir.c_str() );
//     logfile.append ( "/" );
    logfile.append ( fFileNameToCopyNice.c_str() );

    fLog.Debug ( 2,"Creating log direcotry %s ...",logfile.c_str() );
    string mkdirStr ( "mkdir -p " );
    mkdirStr.append ( logfile.c_str() );
//   mkdirStr.append(fFileNameToCopy.c_str());
//   mkdirStr.append(fCurrentFileNameNice.c_str());
    mkdirStr.append ( " > /dev/null 2>&1" );
    int mkdirRetVal = system ( mkdirStr.c_str() );
    if ( mkdirRetVal ) fLog.Error ( "Direcotry %s was created with return value %d...",logfile.c_str(),mkdirRetVal );


    logfile.append ( "/xrd-dm_xrdcp_" );
    char num[10];
    sprintf ( num, "%u", getpid() );
    logfile.append ( num );
    fLog.Debug ( 2,"Log file will be %s ...",logfile.c_str() );


    // real download
    if ( fDebugLevel >= kMoreInfo ) {
        fLog.Info ( "XrdDownloadManager::CopyXrd-> ===============================================" );
        fLog.Info ( "XrdDownloadManager::CopyXrd-> From : %s",fFileNameToDownload[index].c_str() );
        fLog.Info ( "XrdDownloadManager::CopyXrd-> To  : %s",fFileNameToCopyNice.c_str() );
        fLog.Info ( "XrdDownloadManager::CopyXrd-> ===============================================" );
    }


#ifdef HAS_APPMON
    if ( fUseMonalisa )  fAppMon.SetFileFromTo ( fFileNameToDownload[index].c_str(),fFileNameToCopyNice.c_str() );
    if ( fUseMonalisa )  fAppMon.SendInfoMessage ( ( char* ) "Downloading ..." );
#endif

//   fSuccessfullDownloadSpeed=1;

//     if ( fDebugLevel >= kMoreInfo )
//         cout << "XrdDownloadManager::CopyXrd->Log file for download process is " << logfile.c_str() << endl;

//     if ( fDebugLevel >= kFull )
//         cout << "XrdDownloadManager::CopyXrd->Removing file " << fFileNameToCopy.c_str() << " if its already exists..." << endl;
	

    fLog.Debug ( 1,"XrdDownloadManager::CopyXrd->Removing file %s if its already exists...",fFileNameToCopyNice.c_str() );
    remove ( fFileNameToCopyNice.c_str() );

    // do xrdcp here
    string cmd ( "xrdcp -v " );
    if ( !fXrdCpOptions.empty() ) {
        cmd.append ( fXrdCpOptions );
        cmd.append ( " " );
    }

    if ( !fXrdCpOptionsExtra.empty() ) {
        cmd.append ( "-OStried=+" );
        cmd.append ( fXrdCpOptionsExtra );
        cmd.append ( " " );
    }

    cmd.append ( fFileNameToDownload[index].c_str() );
    cmd.append ( " " );
    cmd.append ( fFileNameToCopyNice.c_str() );
    cmd.append ( " > " );
    cmd.append ( logfile.c_str() );
    cmd.append ( " 2>&1 " );


//     if ( fDebugLevel > kMoreInfo ) cout << "XrdDownloadManager::CopyXrd->" << cmd.c_str() << endl;
    fLog.Debug ( 1,"XrdDownloadManager::CopyXrd : Full xrd path is '%s' ",cmd.c_str() );
	cout << "XrdDownloadManager::CopyXrd->" << cmd.c_str() << endl;
    int cpRetVal = system ( cmd.c_str() );

    fLog.Debug ( 1,"XrdDownloadManager::CopyXrd : %s  cpRetVal is %d",cmd.c_str(),cpRetVal );
	
    if ( cpRetVal == 0 ) {

        string cmd2 ( logfile.c_str() );

        FILE *fp;
        char str[128];
        char value[20];
        int i;
        fp = fopen ( cmd2.c_str(), "r" );

        while ( fgets ( str, 126, fp ) ) {
            if ( strstr ( str, "Realtime" ) != NULL ) {
                string strr ( str );
                size_t num = strr.find_first_of ( ":" );
                strr = strr.substr ( num + 1, strr.length() );
                fCurrentFileTime = atof ( strr.c_str() );
            }
        }
        fclose ( fp );

    }

    int retVal = 0;
    switch ( cpRetVal ) {
    case 0:
        fLog.Debug ( 2,"Handling xrdpc val 0 ...." );
        retVal = 0;
//         GetFileSize ( fCurrentFileNameNice );
//         fSuccessfullDownloadSpeed = ( int ) ( double ( fCurrentFileSize ) / fCurrentFileTime / 1024 );
		fSuccessfullDownloadSpeed = 1;
        if ( fDebugLevel > kDebug )
            cout << "XrdDownloadManager::CopyXrd->file copy ok" << endl;
        break;
    case 65280:
        retVal = 1;
        fLog.Debug ( 2,"Handling xrdpc val 65280 ...." );
        fLog.Warning ( "File already exists, removing file %s now...",fFileNameToCopyNice.c_str() );
        remove ( fFileNameToCopyNice.c_str() );
//       fSuccessfullDownloadSpeed = 0;
        break;
    default:
        retVal = 2;
        fLog.Error ( "XrdDownloadManager::CopyXrd->xrdc error" );
		fCurrentFileSize = -1;
        break;

    }
    XrdDownloadManagerDebugFull ( "->" );
    return retVal;
}

string XrdDownloadManager::GetServerFileFullPath ( string fileprefix ) {
    XrdDownloadManagerDebugFull ( "<-" );

    string str ( fServersFileDirName );
    str.append ( "/" );
    str.append ( fileprefix );
    str.append ( fServersFileName );

    XrdDownloadManagerDebugFull ( "->" );
    return str;
}

string XrdDownloadManager::ExecUnix ( string cmd ) {
    XrdDownloadManagerDebugFull ( "<-" );
    FILE* pipe = popen ( cmd.c_str(), "r" );
    if ( !pipe ) return "ERROR";
    char buffer[128];
    string result = "";
    while ( !feof ( pipe ) ) {
        if ( fgets ( buffer, 128, pipe ) != NULL ) {
            result += buffer;

        }
    }
    pclose ( pipe );
    XrdDownloadManagerDebugFull ( "->" );
    return result;
}

void XrdDownloadManager::DeleteServersFile() {
    XrdDownloadManagerDebugFull ( "<-" );
    remove ( GetServerFileFullPath().c_str() );
    XrdDownloadManagerDebugFull ( "->" );
}

int XrdDownloadManager::ReadServersFile() {
    XrdDownloadManagerDebugFull ( "<-" );
    fstream file ( GetServerFileFullPath().c_str(), ios::in );
    if ( !file.is_open() ) {
        if ( fDebugLevel > 0 )
            cerr << "XrdDownloadManager::ReadServersFile->File " << GetServerFileFullPath().c_str() << " was not opened ..." << endl;
        return 0;
    }

    ResetServersFileData();
    int counter = 0;
    int num = fNumColumnsInServersFile;
    string str;
    while ( file >> str ) {
        if ( counter % fNumColumnsInServersFile == 0 ) {
            fSFPath[fNumberOfDataServersInFile++] = str.c_str();
        } else if ( counter % fNumColumnsInServersFile == 1 ) {
            fSFSpeed[fNumberOfDataServersInFile-1] = atoi ( str.c_str() );
        } else if ( counter % fNumColumnsInServersFile == 2 ) {
            fSFAccessNumber[fNumberOfDataServersInFile-1] = atoi ( str.c_str() );
        }
        counter++;
    }
    file.close();
    XrdDownloadManagerDebugFull ( "->" );
    return 0;
}

int XrdDownloadManager::AddServerToServersFile ( string name, int speed, int access ) {
    XrdDownloadManagerDebugFull ( "<-" );
    string outfilename = GetServerFileFullPath ( "tmp_" ).c_str();
    ofstream file ( outfilename.c_str(), ios::app );
    if ( !file.is_open() ) {
        if ( fDebugLevel > kSilent )
            cerr << "XrdDownloadManager::AddServerToServersFile->File " << outfilename.c_str() << " was not opened ..."<< endl;
        return -1;
    }

    fstream fileFirst ( GetServerFileFullPath().c_str(), ios::in );
    if ( !fileFirst.is_open() ) {
//     cout << "**1**" << name << "\t"<< speed << "\t" << 1  << endl;
        file << name << "\t" << speed << "\t" << access << endl;
        file.close();
        rename ( outfilename.c_str(), GetServerFileFullPath().c_str() );
        return 0;
    }

    string str;
    int counter = 0;
    int numAccess = 0;
    int numAccessCurrent = 0;
    string tmpName;
    int tmpSpeedCurrent = speed;
    int tmpSpeed = 0;
    while ( fileFirst >> str ) {
        if ( ( counter % fNumColumnsInServersFile ) == 0 ) {
            tmpName = str.c_str();
        }
        if ( ( counter % fNumColumnsInServersFile ) == 1 ) {
            tmpSpeed = atoi ( str.c_str() );
        }
        if ( ( counter % fNumColumnsInServersFile ) == 2 ) {
//       cout << name<< " "<< GetSiteString(tmpName) << endl;
            size_t found = name.find ( GetSiteString ( tmpName ) );
//       cout << found << endl;
            if ( ! ( found != string::npos ) ) {
//       if (name.find(GetSiteString(tmpName)) == 0) {
                numAccess = atoi ( str.c_str() );
//         cout << "**2**" << tmpName.c_str() << "\t"<< tmpSpeed << "\t" << numAccess  << endl;
                file << tmpName.c_str() << "\t" << tmpSpeed << "\t" << numAccess << endl;
            } else {
//         cout << "**2sp2**" << tmpName.c_str() << "\t"<< tmpSpeed << "\t" << numAccessCurrent  << endl;
                tmpSpeedCurrent = tmpSpeed;
                numAccessCurrent = atoi ( str.c_str() );
//         numAccessCurrent++;
//         cout << "**2sp2**" << tmpName.c_str() << "\t"<< tmpSpeed << "\t" << numAccessCurrent  << endl;
            }
        }
        counter++;
    }
    fileFirst.close();

//   cout << "**3**" << name << "\t"<< tmpSpeedCurrent << "\t" << numAccessCurrent << endl;
    tmpSpeedCurrent = ( int ) ( ( double ) tmpSpeedCurrent + speed ) / 2;
    numAccessCurrent += access;
//   cout << "**3**" << name << "\t"<< tmpSpeedCurrent << "\t" << numAccessCurrent << endl;
    file << name << "\t" << tmpSpeedCurrent << "\t" << numAccessCurrent << endl;
    file.close();

    rename ( outfilename.c_str(), GetServerFileFullPath().c_str() );

    XrdDownloadManagerDebugFull ( "->" );
    return 0;
}

int XrdDownloadManager::OptimizeServersOrder() {
    XrdDownloadManagerDebugFull ( "<-" );

    if ( fNumberOfFilesToDownload <=1 ) return 0;

    // reading server file
    int retVal = ReadServersFile();
    if ( retVal < 0 ) return -1;

//   sorting
    ino64_t tmpspeed;
    string tmpserver;
    for ( int i = 0;i < fNumberOfDataServersInFile - 1;i++ ) {
        for ( int j = i + 1;j < fNumberOfDataServersInFile;j++ ) {
//       cout << i << " " << j<< endl;
            if ( fSFSpeed[i] < fSFSpeed[j] ) {
                tmpserver = fSFPath[i];
                tmpspeed = fSFSpeed[i];
                fSFPath[i] = fSFPath[j];
                fSFSpeed[i] = fSFSpeed[j];
                fSFPath[j] = tmpserver;
                fSFSpeed[j] = tmpspeed;
            }
        }
    }

    // resorting files to download
    int preferedPos = 0;
    string str;
    bool isFound = false;
    int indexNotFound = 0;

    if ( fUseServersFileTryNewSitesFirst ) {
        for ( int i = 0;i < fNumberOfFilesToDownload;i++ ) {
            isFound = false;
            str = GetSiteString ( fFileNameToDownload[i] );
            for ( int j = 0;j < fNumberOfDataServersInFile;j++ ) {
//       cout << GetSiteString(fFileNameToDownload[i]) << " " << GetSiteString(fSFPath[j])<< endl;
                if ( !str.compare ( GetSiteString ( fSFPath[j] ) ) ) {
                    isFound = true;
                }
            }

            if ( !isFound ) {
//       cout << "NOT FOUND " << fFileNameToDownload[i]<< endl;
                MoveToPositionOfFilesToDownload ( preferedPos++, i );
            }
        }
    }

    for ( int j = 0;j < fNumberOfDataServersInFile;j++ ) {
        for ( int i = 0;i < fNumberOfFilesToDownload;i++ ) {
//       cout << GetSiteString(fFileNameToDownload[i]) << " " << GetSiteString(fSFPath[j])<< endl;
            str = GetSiteString ( fFileNameToDownload[i] );
            if ( !str.compare ( GetSiteString ( fSFPath[j] ) ) ) {
                MoveToPositionOfFilesToDownload ( preferedPos++, i );
                i = preferedPos;
            }
        }
    }

    for ( int i = 0;i < fNumberOfFilesToDownload;i++ ) {
        if ( fDebugLevel > kDebugMore )
            cout << "After sorting " << fFileNameToDownload[i].c_str() << endl;
    }

    XrdDownloadManagerDebugFull ( "->" );

    return 0;
}


void XrdDownloadManager::MoveToPositionOfFilesToDownload ( int pos, int indexfile ) {
    XrdDownloadManagerDebugFull ( "<-" );
//   cout <<"MoveToPositionOfFilesToDownload "<< pos << " " << indexfile<< endl;
    if ( pos == indexfile ) return;

    string tmpStr;
    bool tmpBool;

    tmpStr = fFileNameToDownload[pos];
    tmpBool = fIsFileStaged[pos];

    fFileNameToDownload[pos] = fFileNameToDownload[indexfile];
    fIsFileStaged[pos] = fIsFileStaged[indexfile];

    fFileNameToDownload[indexfile] = tmpStr;
    fIsFileStaged[indexfile] = tmpBool;
    XrdDownloadManagerDebugFull ( "->" );
}

string XrdDownloadManager::GetFileString ( string fullPath ) {
//   XrdDownloadManagerDebugFull("GetFileString(string fullPath) <-");
    if ( fullPath.empty() ) return "";
    size_t num = fullPath.find_first_of ( "//" );
    string str = fullPath.substr ( num + 2, fullPath.length() );
    num = str.find_first_of ( "//" );
    str = str.substr ( num + 1, fullPath.length() );
//   XrdDownloadManagerDebugFull("GetFileString(string fullPath) ->");
    return str;

}

string XrdDownloadManager::GetSiteString ( string fullPath ) {
//   XrdDownloadManagerDebugFull("GetSiteString(string fullPath) <-");
    if ( fullPath.empty() ) return "";
//   XrdDownloadManagerDebugFull("GetSiteString(string fullPath) -> full path is  "<< fullPath.c_str() << endl;

    size_t num = fullPath.find_first_of ( "//" );
    string str = fullPath.substr ( num + 2, fullPath.length() );
    num = str.find_first_of ( "//" );
    str = str.substr ( 0, num );
    num = str.find_first_of ( ":" );
    str = str.substr ( 0, num );
//   XrdDownloadManagerDebugFull("GetSiteString(string fullPath) -> site is " << str.c_str() << endl;
//   XrdDownloadManagerDebugFull("GetSiteString(string fullPath) ->");
    return str;
}


int XrdDownloadManager::ConnectToAliEn() {
    XrdDownloadManagerDebugFull ( "<-" );

    int retval = DoSetAliEnVariables();
    if ( fDebugLevel >= kDebug ) cout << "XrdDownloadManager::DoSetAliEnVariables() returned " << retval << endl;
    if ( retval < 0 ) {
        if ( fDebugLevel >= kSilent ) cout << "XrdDownloadManager::ConnectToAliEn() Err[DoSetAliEnVariables()]-> " << retval << endl;
        return retval;
    }

    if ( fAliEn ) {
        XrdDownloadManagerDebugFull ( "ConnectToAliEn() fAliEn is not null when connecting to it, deleting it first... " );
// 		gLog->Info("ConnectToAliEn() fAliEn is not null when connecting to it, deleting it first... ");
		gLog->Error ( "ConnectToAliEn() fAliEn is not null when connecting to it, deleting it first... " );
        delete fAliEn;
        fAliEn = 0;
    }

    if ( fDebugLevel >= kDebugMore ) cout << "XrdDownloadManager::ConnectToAliEn() fAliEn = GapiUI::MakeGapiUI(true)... " << endl;
    fAliEn = GapiUI::MakeGapiUI ( true );

    if ( !fAliEn ) {
        XrdDownloadManagerDebugFull ( "ConnectToAliEn() fAliEn is not null after creating GapiUI::MakeGapiUI(true)... " );
        if ( fDebugLevel >= kSilent ) cout << "XrdDownloadManager::ConnectToAliEn() Err[if (!fAliEn)]-> " << -1 << endl;
        return -1;
    } else {
        if ( fDebugLevel >= kDebugMore ) cout << "XrdDownloadManager::ConnectToAliEn() fAliEn = GapiUI::MakeGapiUI(true) OK ... " << endl;
    }
    char * alienHost = getenv ( "alien_API_HOST" );
//   if (!alienHost){
//     cout<<"Variable $alien_API_HOST not defined"<<endl;
//     return -1;
//   }

    char * alienPort = getenv ( "alien_API_PORT" );
    if ( !alienPort ) {
        cout << "variable $alien_API_PORT not defined" << endl;
        cout << endl;
        cout << "try to do \n\n\tsource /tmp/gclient_env_$UID\n" << endl;
        return -2;
    }

    char * alienUser = getenv ( "alien_API_USER" );
    if ( !alienUser ) {
        cout << "variable $alien_API_USER not defined" << endl;
        cout << endl;
        cout << "try to do source /tmp/gclient_env_$UID" << endl;
        return -3;
    }

    if ( fDebugLevel >= kDebugMore ) {
        cout << "XrdDownloadManager::ConnectToAliEn() Before fAliEn->Connect() " << endl;
        cout << flush;
    }
    fAliEn->Connect ( alienHost, atoi ( alienPort ), alienUser, "0" );
    if ( fDebugLevel >= kDebugMore ) {
        cout << "XrdDownloadManager::ConnectToAliEn() After fAliEn->Connect() " << endl;
        cout << flush;
    }

    if ( !fAliEn->Connected() ) {
        if ( fDebugLevel >= kMoreInfo ) {
            cout << "XrdDownloadManager::ConnectToAliEn() AliEn NOT Connected Retry ... " << endl;
            cout << flush;
        }
//     int retry = fAliEnConnectRetry;
        int retry = 0;
        while ( ++retry <= fAliEnConnectRetry ) {
            if ( fDebugLevel >= kMoreInfo ) {
                cout << "XrdDownloadManager::ConnectToAliEn() Retry (" << retry << " of " << fAliEnConnectRetry << ") ... " << endl;
                cout << flush;
            }
            if ( fDebugLevel >= kDebugMore ) {
                cout << "XrdDownloadManager::ConnectToAliEn() Sleeping 5 secs... " << endl;
                cout << flush;
            }
            sleep ( 5 );
            if ( fDebugLevel >= kDebugMore ) {
                cout << "XrdDownloadManager::ConnectToAliEn() Before fAliEn->Connect() " << endl;
                cout << flush;
            }
            fAliEn->Connect ( alienHost, atoi ( alienPort ), alienUser, "0" );
            if ( fDebugLevel >= kDebugMore ) {
                cout << "XrdDownloadManager::ConnectToAliEn() Before fAliEn->Connect() " << endl;
                cout << flush;
            }
            if ( fAliEn->Connected() ) {
                if ( fDebugLevel >= kDebugMore ) {
                    cout << "XrdDownloadManager::ConnectToAliEn() fAliEn->Connect() OK " << endl;
                    cout << flush;
                }
                break;
            }
        }

        if ( !fAliEn->Connected() ) {
            XrdDownloadManagerDebugFull ( "ConnectToAliEn() fAliEn->Connected() is false ... " );
            XrdDownloadManagerDebugFull ( "ConnectToAliEn() Returning -1" );
            cout << flush;
            delete fAliEn;
            fAliEn = 0;
            if ( fDebugLevel >= kSilent ) cout << "XrdDownloadManager::ConnectToAliEn() Err[if (!fAliEn->Connected())]-> " << retval << endl;
            return -1;
        }
    } else {
        if ( fDebugLevel >= kDebugMore ) {
            cout << "XrdDownloadManager::ConnectToAliEn() AliEn Connected OK ..." << endl;
            cout << flush;
        }
    }

    XrdDownloadManagerDebugFull ( "->" );
    return 1;
}

int XrdDownloadManager::AddFileToDownloadFromAliEn ( string filename ) {
    XrdDownloadManagerDebugFull ( "<-" );

	fLog.Debug(2,"whereis START ...");
	ConnectToAliEn();
    if ( !fAliEn ) {
        fLog.Error ( "fAliEn = null" );
        return -1;
    }

    string cmd ( "whereis " );
    cmd.append ( filename );
    fCurrentFileNameNice = filename.c_str();
    fLog.Debug ( 2,"Doing %s ...",cmd.c_str() );
    bool retValWhereis = fAliEn->Command ( cmd.c_str() );
    fLog.Debug ( 2,"Doing %s done with %d...",cmd.c_str(),retValWhereis );

	int numberOfReplicas = fAliEn->GetStreamColumns ( 2 );
	string currFileStr;
    fLog.Debug ( 1,"Number of replicas %d ...",numberOfReplicas );
    for ( int i = 0;i < numberOfReplicas;i++ ){
		currFileStr = fAliEn->GetStreamFieldValue ( 2, i, 2 );
		fLog.Debug(1,"Adding %s ...",currFileStr.c_str());
        AddFileToDownload ( currFileStr.c_str());
		
	}

	fLog.Debug(2,"whereis DONE ...");
    if ( fDebugLevel >= kDebug ) cout << "XrdDownloadManager::AddFileToDownloadFromAliEn(string filename) whereis DONE" << endl;

    XrdDownloadManagerDebugFull ( "->" );
}

void XrdDownloadManager::GetFileSize ( string filename ) {
    XrdDownloadManagerDebugFull ( "<-" );
	if (!fAliEn) {
		DoAliEnToken();
		ConnectToAliEn();
	}
    string cmd ( "ls -nal " );
    cmd.append ( filename );
    if ( fDebugLevel >= kDebug ) cout << "XrdDownloadManager::GetFileSize(string filename) fAliEn->Command(cmd.c_str()) Before" << endl;
    fAliEn->Command ( cmd.c_str() );
    if ( fDebugLevel >= kDebug ) cout << "XrdDownloadManager::GetFileSize(string filename) fAliEn->Command(cmd.c_str()) Before" << endl;

    fCurrentFileSize = ( double ) atof ( fAliEn->GetStreamFieldValue ( 2, 0, 7 ) );
    XrdDownloadManagerDebugFull ( "->" );
}

bool XrdDownloadManager::IsTokenExpiring() {
    int uid = getuid();
    char token[30];
    sprintf ( token, "/tmp/gclient_token_%u", uid );

    ifstream is ( token );

    if ( !is ) {
        // Token file cannot be opened
        return true;
    }

    char buf[200];
    time_t expire_ts = 0;  // UNIX timestamp

    while ( !is.eof() ) {
        is.getline ( buf, 100 );
        if ( strstr ( buf, "Expiretime = " ) == buf ) {
            expire_ts = atoi ( &buf[13] );
            break;
        }
    }

    is.close();

    if ( expire_ts == 0 ) {
        // No expiretime could be read from token file
        return true;
    }

    if ( expire_ts-fRenewCertificateTime < time ( NULL ) ) {
        return true;
    }

    // Token is not expiring
    return false;
}

bool XrdDownloadManager::TokenInit ( bool showProcess ) {
    char cmd[50];
    sprintf ( cmd, "alien-token-init %s %s", fAliEnUserName.c_str(),
              ( showProcess ? "" : "> /dev/null 2>&1" ) );
    if ( system ( cmd ) ) {
        // Some error occured
        return false;
    }
    return true;
}

bool XrdDownloadManager::IsProxyExpiring() {
    char cmd[200];
    int uid = getuid();
    char proxy[30];
    int r;

    sprintf ( proxy, "/tmp/x509up_u%u", uid );

    // openssl -checkend returns 0 if x509 cert will be valid in the next %d
    // seconds, or 1 if either proxy cannot be read or it is expiring
    sprintf ( cmd, "openssl x509 -in \"%s\" -noout -checkend %d > /dev/null 2>&1",
              proxy, fRenewCertificateTime );

    if ( system ( cmd ) ) {
        return true;
    }

    return false;
}

bool XrdDownloadManager::ProxyInit ( bool showProcess ) {
    char cmd[50];

    if ( system ( "which xrdgsiproxy" ) ) {
        strcpy ( cmd, "grid-proxy-init" );
    } else {
        strcpy ( cmd, "xrdgsiproxy init" );
    }

    if ( !showProcess ) {
        strcat ( cmd, " > /dev/null 2>&1" );
    }

    if ( system ( cmd ) ) {
        return false;
    }

    return true;
}

int XrdDownloadManager::DoAliEnToken ( bool showProcess ) {
	
    if ( !fDoAliEnToken ) return 0;

    if ( IsTokenExpiring() ) {
        fLog.Debug (1, "Renewing AliEn token..." );
        if ( TokenInit ( showProcess ) ) {
            fLog.Debug (1, "AliEn token renewed successfully" );
        } else {
            fLog.Error ( "Cannot renew AliEn token" );
            return -1;
        }
    } else {
        fLog.Debug (1, "Reusing existing token" );
    }

    if ( IsProxyExpiring() ) {
        fLog.Debug (1, "Renewing Grid proxy..." );
        if ( ProxyInit ( showProcess ) ) {
            fLog.Debug (1, "Grid proxy renewed successfully" );
        } else {
            fLog.Error ( "Cannot renew Grid proxy" );
            return -2;
        }
    } else {
        fLog.Debug (1, "Reusing existing proxy" );
    }
}


int XrdDownloadManager::DoSetAliEnVariables() {
    XrdDownloadManagerDebugFull ( "DoSetAliEnVariables() <-" );

//   DoAliEnToken();

    string filename ( "/tmp/gclient_env_" );
    char uid[10];
    sprintf ( uid, "%u", getuid() );
    filename.append ( uid );
    fstream file ( filename.c_str(), ios::in );
    if ( !file.is_open() ) {
        if ( fDebugLevel > kSilent )
            cerr << "File " << filename.c_str() << " was not opened ..." << endl;
        return -1;
    }
    size_t found;
    string str, var, val;
    while ( file >> str ) {

        found = str.find ( "export" );
        if ( found != string::npos ) {
            file >> str;
            found = str.find_first_of ( "=" );
            var = str.substr ( 0, found );
            val = str.substr ( found + 1, str.length() );
            found = val.find ( "\"" );
            if ( found != string::npos ) {
                val.replace ( 0, 1, "" );
                val.replace ( val.length() - 1, 1, "" );
            }
            found = var.find ( "LD_LIBRARY_PATH" );
            if ( ! ( found != string::npos ) ) {
                setenv ( var.c_str(), val.c_str(), 1 );
            }
        }

    }
    file.close();
    XrdDownloadManagerDebugFull ( "DoSetAliEnVariables() ->" );
    return 0;
}

int XrdDownloadManager::ReadConfigFile ( string cfgName ) {
    XrdDownloadManagerDebugFull ( "<-" );
    fstream file ( cfgName.c_str(), ios::in );
    if ( !file.is_open() ) {
        cerr << "XrdDownloadManager::ReadConfigFile->File " << cfgName.c_str() << " was not opened ..." << endl;
        return -1;
    }

    const short n = 256;
    char char_str[n];
    string str, var, val;
    size_t found;
    while ( file.getline ( char_str, n ) ) {
        str = char_str;
        if ( str.substr ( 0, 5 ).compare ( "xrddm" ) )
            continue;
        found = str.find ( "xrddm" );
        if ( found != string::npos ) {
            found = str.find_first_of ( "=" );
            var = str.substr ( 0, found );
            val = str.substr ( found + 1, str.length() );
            found = val.find ( "\"" );
            if ( found != string::npos ) {
                val.replace ( 0, 1, "" );
                val.replace ( val.length() - 1, 1, "" );
            }
            if ( fDebugLevel >= kFull )
                cout << "XrdDownloadManager::ReadConfigFile(string cfgName) -> SetConfigOptions(\"" << var << "\",\"" << val << "\")" << endl;
            SetConfigOptions ( var, val );
        }

    }
    file.close();

    if ( fDebugLevel > kFull )
        PrintXrddmOptions();

	InitLogFile();
	
    XrdDownloadManagerDebugFull ( "ReadConfigFile(string cfgName) ->" );
    return 0;
}


void XrdDownloadManager::SetConfigOptions ( string var, string val ) {
//     XrdDownloadManagerDebugFull("SetConfigOptions("<< var.c_str()<<", "<< val.c_str()<<") <-");


    if ( var.compare ( "xrddm.DebugLevel" ) == 0 )
        SetDebug ( atoi ( val.c_str() ) );
    else if ( var.compare ( "xrddm.CreateAliEToken" ) == 0 )
        CreateAliEnToken ( ( bool ) atoi ( val.c_str() ) );
    else if ( var.compare ( "xrddm.AliEnUserName" ) == 0 )
        SetAliEnUserName ( val );
    //else if (var.compare("xrddm.AliEnPassword") == 0)
    //  SetAliEnPassword(val);
    else if ( var.compare ( "xrddm.RenewCertificateTime" ) == 0 )
        SetRenewCertificateTime ( atoi ( val.c_str() ) );
    else if ( var.compare ( "xrddm.LogDir" ) == 0 )
        SetLogDir ( val );
    else if ( var.compare ( "xrddm.XrdOptions" ) == 0 )
        SetXrdCpOptions ( val );
    else if ( var.compare ( "xrddm.AppMonConfig" ) == 0 )
        SetAppMonConfigFile ( val );
    else if ( var.compare ( "xrddm.AppMonName" ) == 0 ) {
        SetAppMonName ( val );
    } else
        cerr << "XrdDownloadManager::SetConfigOptions(string var, string val) ->Option " << var << " is not supported!!!" << endl;

//     XrdDownloadManagerDebugFull("SetConfigOptions("<< var.c_str()<<", "<< val.c_str()<<") ->");
}


void XrdDownloadManager::PrintXrddmOptions() {
    XrdDownloadManagerDebugFull ( "Print() <-" );

    fLog.Info ( "================ Options =====================" );
    fLog.Info ( "Log Directory                       : %s",fLogDir.c_str() );
    fLog.Info ( "DoAliEnToken (-a)                   : %d",fDoAliEnToken );
    fLog.Info ( "UseFallbackDownload (-f)            : %d",fUseFallbackDownload );
    fLog.Info ( "UseServersFile (-s)                 : %d",fUseServersFile );
    fLog.Info ( "UseServersFileTryNewSitesFirst (-n) : %d",fUseServersFileTryNewSitesFirst );
    fLog.Info ( "UseAliEnCp (-a)                     : %d",fUseAliEnCp );
    fLog.Info ( "Use Monalisa (-m)                   : %d",fUseMonalisa );
    fLog.Info ( "AliEn Username                      : %s",fAliEnUserName.c_str() );
    fLog.Info ( "AliEn Password                      : %s",fAliEnPassword.c_str() );
    fLog.Info ( "Renew Certificate Time (s)          : %d",fRenewCertificateTime );
    fLog.Info ( "XrdCpOptions                        : %s",fXrdCpOptions.c_str() );
    fLog.Info ( "AppMonConfig                        : %s",fAppMonConfigFile.c_str() );
    fLog.Info ( "AppMonName                          : %s",fAppMonName.c_str() );
    fLog.Info ( "==============================================" );

    XrdDownloadManagerDebugFull ( "->" );
}

void XrdDownloadManager::FindAndReplaceString ( string &source, const string find, string replace ) {

    size_t j;
    for ( ; ( j = source.find ( find ) ) != string::npos ; ) {
        source.replace ( j, find.length(), replace );
    }
}

void XrdDownloadManager::PrintDownloadInfo() {
    fLog.Info ( "From       : %s",fFileNameFromCopyNice.c_str() );
    for ( int i = 0;i < fNumberOfFilesToDownload;i++ ) {
        AddServerToServersFile ( fFileNameToDownload[i], 0, 0 );
        fLog.Info ( "           : %s",fFileNameToDownload[i].c_str() );
    }
    fLog.Info ( "To         : %s",fFileNameToCopyNice.c_str() );

    time_t rawtime;
    struct tm * timeinfo;
    char buffer [80];
    time ( &rawtime );
    timeinfo = localtime ( &rawtime );
	strftime ( buffer,80,"%c",timeinfo );
	string timestrtmp = buffer;
	gLog->Info ("xrd-dm %s to %s started at %s",fFileNameFromCopyNice.c_str(),fFileNameToCopyNice.c_str(),timestrtmp.c_str());
}

void XrdDownloadManager::SetFileIOFileNames ( string in, string out ) {
    XrdDownloadManagerDebugFull ( "<-" );
    fFileNameFromCopyNice = in;
    fFileNameToCopyNice = out;
    XrdDownloadManagerDebugFull ( "->" );
}

